import md2d : md2d, parseMD;
import std.stdio;
import std.file;
import std.string;
import factories : modules;
import genapi;

string[string] ids;
string[][string] names;

void readMD(string file) {
	import md2d.parser;

	alias BT = BlockType;

	auto md = cast(string)read(file);
	Block root;
	parseMD(md, root);
	string key;
	foreach (b; root.blocks) {
		if (b.type == BT.Header && b.headerLevel > 1) {
			const title = b.text[0].strip;
			if (title == "Items枚举")
				key = "names";
			else if (title == "Terrain枚举")
				key = "terrains";
			else if (title == "Soil枚举")
				key = "soils";
			else if (title == "Status枚举")
				key = "errors";
			if (key.length && key !in names)
				names[key] = [];
		} else if (key.length && b.type == BT.Table) {
			foreach (r; b.blocks) {
				if (r.type == BT.TableRow) {
					const blocks = r.blocks;
					string val;
					for (size_t i; i < blocks.length; i++) {
						auto c = blocks[i];
						if (c.type == BT.TableData) {
							if (val.length) {
								const text = c.text[0].strip;
								if (key == "names")
									ids[text] = val;
								names[key] ~= text;
								break;
							}
							val = c.text[0].strip;
						}
					}
				}
			}
			key = null;
		}
	}
}

enum N = 100;

void convertProcs(string file) {
	import std.algorithm;
	import std.conv;

	readMD("./docs/enums.md");
	auto output = File("./factories/procs.d", "wb");
	output.writeln("// Generated by build.d
module factories.procs;
import factories.storage;
import factories.generated;
import factories.util;
");
	uint[] indices;
	auto data = "[";
	uint i, n, m;
	auto input = File(file);
	foreach (line; input.byLine) {
		if (i) {
			auto s = line.split(',');
			if (s.length < 3)
				continue;
			indices ~= m;
			foreach (item; s[1].splitter("、")) {
				uint count = parse!uint(item);
				if (item !in ids) {
					throw new Exception("Unknown item: " ~ cast(string)item);
				}
				data ~= text("ItemDelta(Items.", ids[item], ", -", count, "),");
				m++;
			}
			foreach (item; s[2].splitter("、")) {
				uint count = parse!uint(item);
				if (item !in ids) {
					throw new Exception("Unknown item: " ~ cast(string)item);
				}
				data ~= text("ItemDelta(Items.", ids[item], ", ", count, "),");
				m++;
			}
			n++;
		}
		i++;
	}
	n += N;
	m += N;
	output.writeln("enum uint[", n, "] indices =", indices, ';');
	output.writeln("enum ItemDelta[", m, "] data = ", data, "];");
	output.writeln("auto procs=MultiArray!(ItemDelta,", n, ",", m, ")(indices,data);");
}

/// 建筑
void parseBuildings(File input, File output) {
	uint i;
	foreach (line; input.byLine) {
		if (i) {
			auto s = line.strip.split(',');
			if (s[0].length && s[5].length)
				output.writeln("{
	id: ", s[5], ",
	name: '", s[0], "',
	desc: '时代：", s[4], "',
	cost: '", s[1].replace("、", ","), "',
	builtOn: ", s[6], ",
	time: 2,
},");
		}
		i++;
	}
}

/// 科技树
void parseTechs(File input, File output) {
	uint i;
	foreach (line; input.byLine) {
		if (i) {
			auto s = line.strip.split(',');
			if (s[0].length)
				output.writeln("{
	name: '", s[0], "',
	prereqs: [", s[1].length ? '\'' ~ s[1].replace("、", "','") ~ '\'' : "", "],
	time: ", s[2], ",
},");
		}
		i++;
	}
}

/// 解析配方
void parseRecipes(File input, File output) {
	uint i;
	foreach (line; input.byLine) {
		if (i) {
			auto s = line.strip.split(',');
			if (s[0].length)
				output.writeln("
	recipe('", s[0], "','", s[3].replace("、", ","), "',
	[");
			if (s[1].length)
				output.write("...parseRecipe('", s[1], "',-1),");
			if (s[2].length)
				output.write("...parseRecipe('", s[2], "',1)");
			output.write("
],", s[4], ",'", s[5], "'),");
		}
		i++;
	}
}

void genDefs() {
	auto output = File("./src/defs.ts", "wb");
	enum STORAGE_TYPES = StorageType.max + 1,
		ITEMS_MAX = Items.max + 1;
	output.writeln("// Generated by build.d
import { recipes, recipe, parseRecipe } from './recipes'
import { Building } from './type'

export const
	STORAGE_TYPES = ", STORAGE_TYPES, ",
	ITEMS_MAX = ", ITEMS_MAX, ",
	STORAGE_SIZE = ", STORAGE_TYPES + ITEMS_MAX, ",
	mapWidth = 64,
	mapSize = mapWidth ** 2
");
	foreach (key, val; names) {
		output.write("export const ", key, " = [");
		foreach (name; val) {
			output.write("'", name, "', ");
		}
		output.writeln("]");
	}
	output.writeln("
/** 名称到 ID 的映射 */
export const ids = new Map<string, number>(names.map((name, i) => [name, i]))
");
	output.writeln("
/** 建筑 */
export const buildings: Building[] = [");
	parseBuildings(File("./docs/buildings.csv"), output);
	parseBuildings(File("./docs/factories.csv"), output);
	output.writeln("]
/** 科技树 */
export const techs = [");
	parseTechs(File("./docs/techs.csv"), output);
	output.writeln("]

export interface Exports extends WebAssembly.Exports {
");
	auto app = output.lockingTextWriter();
	ForModules!modules.genAPIDef(app);
	output.write("
	memory: WebAssembly.Memory
}

/** 初始化配方 */
export function initRecipes() {
	recipes.push(");
	parseRecipes(File("./docs/recipes.csv"), output);
	output.writeln(")
}
");
}

enum file = "./docs/enums.md";
enum code = md2d(import(file));
mixin(code);

void main() {
	auto output = File("./factories/generated.d", "wb");
	output.write("module factories.generated;\n", code);
	convertProcs("./docs/recipes.csv");
	genDefs();
}
